package com.jsh.erp.service.redis;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.jsh.erp.constants.BusinessConstants;
import com.jsh.erp.constants.CacheConstant;
import com.jsh.erp.utils.StringUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Description
 *
 * @author jisheng hua
 * @Date: 2021/1/28 18:10
 */
@Component
public class RedisService {

    //    @Resource
    public RedisTemplate redisTemplate;

    private Cache<String, Object> cache;

    public static final String ACCESS_TOKEN = "X-Access-Token";

    //    @Autowired(required = false)
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(stringSerializer);
        this.redisTemplate = redisTemplate;
    }

    @PostConstruct
    private void initCache() {
        cache = Caffeine.newBuilder()
                // 设置最后一次写入或访问后经过固定时间过期
                .expireAfterWrite(BusinessConstants.MAX_SESSION_IN_SECONDS, TimeUnit.SECONDS)
                // 初始的缓存空间大小
                .initialCapacity(100)
                // 缓存的最大条数
                .maximumSize(2000)
                .build();
    }

    public void put(String k, String v) {
        cache.put(k, v);
    }


    public String get(String k) {
        return (String) cache.get(k, v -> "");
    }

    /**
     * @return Object
     * @author jisheng hua
     * description:
     * 从session中获取信息
     * @date: 2021/1/28 18:10
     * @Param: request
     * @Param: key
     */
    public Object getObjectFromSessionByKey(HttpServletRequest request, String key) {
        Object obj = null;
        if (request == null) {
            return null;
        }
        String token = request.getHeader(ACCESS_TOKEN);
        if (token != null) {
            if (redisTemplate != null) {
                //开启redis，用户数据放在redis中，从redis中获取
                if (redisTemplate.opsForHash().hasKey(token, key)) {
                    //redis中存在，拿出来使用
                    obj = redisTemplate.opsForHash().get(token, key);
                    redisTemplate.expire(token, BusinessConstants.MAX_SESSION_IN_SECONDS, TimeUnit.SECONDS);
                }
            } else {
                Object o = cache.get(token, k -> null);
                if (o != null) {
                    obj = ((HashMap) o).getOrDefault(key, null);
                }
            }
        }
        return obj;
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key) {
        if (redisTemplate != null) {
            ValueOperations<String, T> operation = redisTemplate.opsForValue();
            return operation.get(key);
        } else {
            return (T) cache.get(key, k -> null);
        }
    }

    /**
     * @return
     * @author jisheng hua
     * description:
     * 将信息放入session或者redis中
     * @date: 2021/1/28 18:10
     * @Param: request
     * @Param: key
     * @Param: obj
     */
    public void storageObjectBySession(String token, String key, Object obj) {
        if (redisTemplate != null) {
            //开启redis，用户数据放到redis中
            redisTemplate.opsForHash().put(token, key, obj.toString());
            redisTemplate.expire(token, BusinessConstants.MAX_SESSION_IN_SECONDS, TimeUnit.SECONDS);
        } else {
            HashMap<String, Object> o = (HashMap<String, Object>) cache.get(token, k -> new HashMap<String, Object>());
            o.put(key, obj);
        }
    }

    /**
     * @return
     * @author jisheng hua
     * description:
     * 将信息放入session或者redis中
     * @date: 2024/5/28 20:10
     */
    public void storageCaptchaObject(String verifyKey, String codeNum) {
        if (redisTemplate != null) {
            //把验证码放到redis中
            redisTemplate.opsForValue().set(verifyKey, codeNum, BusinessConstants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        } else {
            cache.put(verifyKey, codeNum);
        }
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public void deleteCaptcha(final String key) {
        if (redisTemplate != null) {
            redisTemplate.delete(key);
        } else {
            cache.invalidate(key);
        }
    }

    /**
     * @return
     * @author jisheng hua
     * description:
     * 将信息从session或者redis中移除
     * @date: 2021/1/28 18:10
     * @Param: request
     * @Param: key
     * @Param: obj
     */
    public void deleteObjectBySession(HttpServletRequest request, String key) {
        if (request != null) {
            String token = request.getHeader(ACCESS_TOKEN);
            if (StringUtil.isNotEmpty(token)) {
                if (redisTemplate != null) {
                    //开启redis，用户数据放在redis中，从redis中删除
                    redisTemplate.opsForHash().delete(token, key);
                } else {
                    HashMap<String, Object> o = (HashMap<String, Object>) cache.get(token, k -> new HashMap<String, Object>());
                    o.remove(key);
                }
            }
        }
    }

    /**
     * @param userId
     * @param clientIp
     * @author jisheng hua
     * 将信息从redis中移除，比对user和ip
     */
    public void deleteObjectByUserAndIp(Long userId, String clientIp) {
        if (redisTemplate != null) {
            Set<String> tokens = redisTemplate.keys("*");
            for (String token : tokens) {
                Object userIdValue = redisTemplate.opsForHash().get(token, "userId");
                Object clientIpValue = redisTemplate.opsForHash().get(token, "clientIp");
                if (userIdValue != null && clientIpValue != null && userIdValue.equals(userId.toString()) && clientIpValue.equals(clientIp)) {
                    redisTemplate.opsForHash().delete(token, "userId");
                }
            }
        } else {
            cache.asMap().forEach((k, v) -> {
                if (v instanceof HashMap) {
                    String userIdValue = ((HashMap<String, String>) v).get("userId");
                    String clientIpValue = ((HashMap<String, String>) v).get("clientIp");
                    if (userIdValue != null && clientIpValue != null && userIdValue.equals(userId.toString()) && clientIpValue.equals(clientIp)) {
                        ((HashMap<?, ?>) v).remove("userId");
                    }
                }
            });
        }
    }

    public void expireUser(Long userId) {
        if (userId == null || userId == 0L) {
            return;
        }
        HashSet<Long> set = (HashSet<Long>) cache.get(CacheConstant.USER_ID_EXPIRE, k -> new HashSet<Long>());
        set.add(userId);
        //cache.put("__userId_expire__", set);
    }

    public HashSet<Long> getExpireUserSet() {
        return (HashSet<Long>) cache.get(CacheConstant.USER_ID_EXPIRE, k -> new HashSet<Long>());
    }

    public boolean isUserExpire(Long userId) {
        return getExpireUserSet().contains(userId);
    }
}
